/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.language;

import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import ac.jp.u_tokyo.SyncLib.SyncFactory;
import ac.jp.u_tokyo.SyncLib.graphCombinator.GraphCombinatorFactory;
import ac.jp.u_tokyo.SyncLib.util.Pair;
import ac.jp.u_tokyo.SyncLib.util.StringHelper;

public class GraphCombinatorBody extends SyncBody
{

	List<Var> _paraVars = new LinkedList<Var>();

	Set<Var> _otherVars = new HashSet<Var>();

	Collection<Pair<SyncBody, List<Var>>> _syncConnections = new LinkedList<Pair<SyncBody, List<Var>>>();

	public static class Var {
		String _name;
		int _line;
		@Override
		public int hashCode()
		{
			final int prime = 31;
			int result = 1;
			result = prime * result + ((_name == null) ? 0 : _name.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj)
		{
			if (this == obj) return true;
			if (obj == null) return false;
			if (getClass() != obj.getClass()) return false;
			final Var other = (Var) obj;
			if (_name == null)
			{
				if (other._name != null) return false;
			}
			else if (!_name.equals(other._name)) return false;
			return true;
		}
		public String getName()
		{
			return _name;
		}
		public int getLine()
		{
			return _line;
		}
		public Var(int line, String name)
		{
			super();
			_name = name;
			_line = line;
		}
	}
	
	public GraphCombinatorBody(int line)
	{
		super(line);
	}

	public void addParameters(Collection<Var> paras)
	{
		_paraVars.addAll(paras);
	}
	
	public List<Var> getParameters() {
		return _paraVars;
	}

	public void addOtherVars(Collection<Var> vars)
	{
		_otherVars.addAll(vars);
	}
	
	public void addOtherVar(Var vars)
	{
		_otherVars.add(vars);
	}


	public void addConnection(SyncBody body, List<Var> vars)
	{
		_syncConnections.add(new Pair<SyncBody, List<Var>>(body, vars));
	}

	static String getFactoryFullName()
	{
		return GraphCombinatorFactory.class.getName();
	}

	@Override
	public String writeInitializeFactory(Appendable sb,
			VarNameGenerator generator, ConstMapper constMap)
			throws EvaluationFailedException, IOException
	{
		String varName = generator.getNextName();
		sb.append(getFactoryFullName()).append(' ').append(varName).append(
				" = new ").append(getFactoryFullName()).append("();\n");
		sb.append(varName).append(".setParaVars(");
		writeVarList(sb, _paraVars);
		sb.append(");\n");
		
		sb.append(varName).append(".setOtherVars(");
		writeVarList(sb, _otherVars);
		sb.append(");\n");
		
		for(Pair<SyncBody, List<Var>> c : _syncConnections) {
			String innerSyncName = c.getValue1().writeInitializeFactory(sb, generator, constMap);
			sb.append(varName).append(".connectSync(").append(innerSyncName).append(", ");
			checkVarDeclaration(c.getValue2());
			if (c.getValue2().size() != c.getValue1().getParaCount()) {
				throw new ParameterNumberNotMatchException(c.getValue1().getLine(), c.getValue1().getParaCount());
			}
			writeVarList(sb, c.getValue2());
			sb.append(");\n");
		}
		return varName;
	}

	private void checkVarDeclaration(Iterable<Var> value2) throws ReferenceNotFoundException
	{
		for (Var v : value2) {
			if (!_paraVars.contains(v) && !_otherVars.contains(v)) {
				throw new ReferenceNotFoundException(v.getLine(), v.getName());
			}
		}
		
	}

	private void writeVarList(Appendable sb, Iterable<Var> vars)
			throws IOException
	{
		Iterator<Var> it = vars.iterator();
		if (it.hasNext())
			sb.append(StringHelper.getJavaString(it.next().getName()));
		while(it.hasNext())
			sb.append(", ").append(StringHelper.getJavaString(it.next().getName()));
	}

	@Override
	public int getParaCount() throws EvaluationFailedException
	{
		return _paraVars.size();
	}

}
